﻿// -------------------------------------------------------------------------------------------------------------------------------
// <copyright file="CrcsSparseSymmetricMatrix{T}.cs" company="Exul">
//     Copyright (c) Exul. All rights reserved.
// </copyright>
// <author>
//     Exul
// </author>
// -------------------------------------------------------------------------------------------------------------------------------

namespace ExulLibrary.Mathematics.LinearAlgebra
{
    #region Usings

    using System;

    using ExulLibrary.Exceptions;

    #endregion

    /// <summary>
    ///     Represents a sparse symmetric matrix with the compressed storage with row-major order for the low triangle and
    ///     column-major order for the high triangle.
    /// </summary>
    /// <typeparam name="T">
    ///     The type of values of matrix elements.
    /// </typeparam>
    public sealed class CrcsSparseSymmetricMatrix<T> : Matrix<T>
    {
        #region Fields

        #region private

        /// <summary>
        ///    The registration index that uniquely identify matrix type.
        /// </summary>
        private static readonly int RegistrationIndex =
            Global.GetMatrixRegistrationIndex(typeof(CrcsSparseSymmetricMatrix<object>));

        #endregion

        #endregion

        #region Constructors and Destructors

        #region public

        /// <summary>
        ///     Initializes a new instance of the <see cref="CrcsSparseSymmetricMatrix{T}"/> class.
        /// </summary>
        /// <param name="begins">
        ///     The collection of indexes of rows and columns begins.
        /// </param>
        /// <param name="indexes">
        ///     The collection of zero-based indexes of rows and columns of nonzero elements.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="begins"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="indexes"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        /// <exception cref="NoEnoughElementsException">
        ///     <paramref name="begins"/> has count of elements less than 2.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="indexes"/> has count of elements less than
        ///         <paramref name="begins"/>[rowsCount] - <paramref name="begins"/>[0].
        ///     </para>
        /// </exception>
        /// <remarks>
        ///     The size of matrix is determined as length of <paramref name="begins"/> minus 1.
        ///     The elements of <paramref name="begins"/> should be nonnegative and
        ///     each element of <paramref name="begins"/> should be equal to or greater than it's previous elements,
        ///     but both conditions don't verified in the constructor.
        /// </remarks>
        public CrcsSparseSymmetricMatrix(int[] begins, int[] indexes)
            : this(begins, indexes, null, null)
        {
        }

        /// <summary>
        ///     Initializes a new instance of the <see cref="CrcsSparseSymmetricMatrix{T}"/> class.
        /// </summary>
        /// <param name="begins">
        ///     The collection of indexes of rows and columns begins.
        /// </param>
        /// <param name="indexes">
        ///     The collection of zero-based indexes of rows and columns of nonzero elements.
        /// </param>
        /// <param name="diagonalData">
        ///     The collection of values of elements on diagonal.
        /// </param>
        /// <param name="triangleData">
        ///     The collection of values of nonzero elements placed not on diagonal.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="begins"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="indexes"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        /// <exception cref="NoEnoughElementsException">
        ///     <paramref name="begins"/> has count of elements less than 2.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="indexes"/> has count of elements less than
        ///         <paramref name="begins"/>[rowsCount] - <paramref name="begins"/>[0].
        ///     </para>
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="triangleData"/> is not <see langword="null"/> and has count of elements less than
        ///         <paramref name="begins"/>[rowsCount] - <paramref name="begins"/>[0].
        ///     </para>
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="diagonalData"/> is not <see langword="null"/> and has count of elements less than rows count.
        ///     </para>
        /// </exception>
        /// <remarks>
        ///     The size of matrix is determined as length of <paramref name="begins"/> minus 1.
        ///     The elements of <paramref name="begins"/> should be nonnegative and
        ///     each element of <paramref name="begins"/> should be equal to or greater than it's previous elements,
        ///     but both conditions don't verified in the constructor.
        /// </remarks>
        public CrcsSparseSymmetricMatrix(int[] begins, int[] indexes, T[] diagonalData, T[] triangleData)
        {
            #if SAFE

            if (begins == null)
            {
                throw new ArgumentNullException("begins");
            }

            if (indexes == null)
            {
                throw new ArgumentNullException("indexes");
            }

            if (begins.Length < 2)
            {
                throw new NoEnoughElementsException("begins", begins.Length, 2);
            }

            #endif

            this.RowsCount = this.ColumnsCount = begins.Length - 1;
            var elementsCount = begins[this.RowsCount] - begins[0];

            #if SAFE

            if (indexes.Length < elementsCount)
            {
                throw new NoEnoughElementsException("indexes", indexes.Length, elementsCount);
            }

            if ((triangleData != null) && (triangleData.Length < elementsCount))
            {
                throw new NoEnoughElementsException("triangleData", triangleData.Length, elementsCount);
            }

            if ((diagonalData != null) && (diagonalData.Length < begins.Length - 1))
            {
                throw new NoEnoughElementsException("diagonalData", diagonalData.Length, begins.Length - 1);
            }

            #endif

            this.Begins = begins;
            this.Indexes = indexes;
            this.TriangleValues = triangleData ?? new T[elementsCount];
            this.DiagonalValues = diagonalData ?? new T[begins.Length - 1];
        }

        #endregion

        #endregion

        #region Properties

        #region public

        /// <summary>
        ///     Gets the collection of indexes of rows and columns begins.
        /// </summary>
        /// <value>
        ///     The collection of indexes of rows and columns begins.
        /// </value>
        public int[] Begins
        {
            get;
            private set;
        }

        /// <summary>
        ///     Gets the collection of values of diagonal elements.
        /// </summary>
        /// <value>
        ///     The collection of values of diagonal elements.
        /// </value>
        public T[] DiagonalValues
        {
            get;
            private set;
        }

        /// <summary>
        ///     Gets the collection of zero-based indexes of rows and columns of nonzero elements.
        /// </summary>
        /// <value>
        ///     The collection of zero-based indexes of rows and columns of nonzero elements.
        /// </value>
        public int[] Indexes
        {
            get;
            private set;
        }

        /// <summary>
        ///     Gets the collection of values of nonzero elements.
        /// </summary>
        /// <value>
        ///     The collection of values of nonzero elements.
        /// </value>
        public T[] TriangleValues
        {
            get;
            private set;
        }

        #endregion

        #endregion

        #region Overridden Properties

        #region public

        /// <summary>
        ///     Gets the registration index of the matrix that uniquely identifies type of matrix.
        /// </summary>
        /// <value>
        ///     The registration index of the matrix that uniquely identifies type of matrix.
        /// </value>
        public override int MatrixRegistrationIndex
        {
            get
            {
                return RegistrationIndex;
            }
        }

        #endregion

        #endregion
    }
}